Skip to content

Add: export Worker from the simpler package root - #1535

Merged
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoWao:feat/export-user-api
Jul 28, 2026
Merged

Add: export Worker from the simpler package root#1535
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoWao:feat/export-user-api

Conversation

@ChaoWao

@ChaoWao ChaoWao commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

simpler.__all__ advertised only the logging helpers, so the runtime's own entry point was undiscoverable — from simpler import Worker failed, and you had to already know to write from simpler.worker import Worker. Meanwhile the package docstring calls itself the "public Python surface".

Worker and the task_interface submodule now resolve through a PEP 562 module __getattr__ instead of eager imports.

Why lazy rather than a plain import

Both simpler/worker.py:83 and simpler/task_interface.py:35 import _task_interface unguarded (and worker additionally imports cloudpickle). Importing either from __init__.py would therefore make import simpler fail outright whenever that extension is missing or stale — including for callers that only want the logging helpers, which _log.py:42-45 deliberately keeps working through a try/except fallback.

This is not hypothetical: on this dev box a stale .so already makes import simpler.worker raise cannot import name 'TENSOR_CHILD_MEMORY_OFFSET', while import simpler keeps working. Eager exports would have converted that into "the whole package is unimportable".

Measured: import simpler stays at ~0.02 s and does not load simpler.worker.

Deliberately not flattened

The task and callable types stay in simpler.task_interface. simpler.task_interface.Tensor is a nanobind device-tensor descriptor (Tensor.make(ptr, shape, dtype)) while simpler_setup.Tensor is a scene-test argument-spec NamedTuple (name, value) — same name, unrelated types. Hoisting both into one namespace would invite exactly the confusion this export is meant to remove.

project-layout.md corrected

The rule described simpler_setup as internal test scaffolding. It is user-facing: examples import KernelCompiler (×15), ensure_pto_isa_root (×15), make_tensor_arg (×11) and the SceneTestCase group (×7) from it — more often than they import from simpler — and a kernel cannot be compiled without it. The table now says so and records the Tensor collision.

Tests

New tests/ut/py/test_package_surface.py pins the surface: what __all__ advertises, that unknown attributes still raise AttributeError, that simpler.Worker is the same object as simpler.worker.Worker, and the two properties the laziness exists for — import simpler does not pull in simpler.worker, and it still succeeds with no usable extension.

One detail worth reviewing: the first of those asserts on simpler.worker, not on _task_interface. My initial version asserted the latter and failed — because _log's guarded probe legitimately puts the extension in sys.modules whenever it is importable. The weaker signal would have been a false alarm forever.

  • ruff check and ruff format --check clean on both changed Python files
  • New tests: 5 passed, 1 skipped (the skip is the extension-dependent identity check, correctly skipping against this box's stale .so)
  • Verified against an unshadowed copy of the package, because this venv's editable-install finder shadows PYTHONPATH
  • Full CI — this touches python/ so the complete pipeline runs

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 19811e2b-b6f8-47f4-8ade-c01a18e6c500

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The package root now lazily exposes Worker and task_interface, preserves existing logging exports, updates import documentation, and adds tests for public-surface visibility and import behavior when worker modules or extensions are unavailable.

Changes

Lazy package surface

Layer / File(s) Summary
Public exports and lazy resolution
python/simpler/__init__.py, .claude/rules/project-layout.md, docs/user/README.md, docs/user/reference/python-api.md
Worker and task_interface are advertised and resolved lazily, with documentation covering import paths and distinct Tensor types.
Public surface validation
tests/ut/py/test_package_surface.py
Tests verify exports, logging helpers, deferred imports, missing-extension behavior, unknown attributes, and Worker identity.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant simpler
  participant importlib
  participant simpler_worker
  participant task_interface

  Caller->>simpler: import simpler
  Caller->>simpler: access Worker or task_interface
  simpler->>importlib: import requested module
  importlib->>simpler_worker: load Worker
  importlib->>task_interface: load task_interface
  simpler-->>Caller: return requested export
Loading

Poem

I’m a rabbit with exports tucked light,
Worker appears when the moment is right.
Task types hop from their interface home,
While logging stays safely known.
Two Tensors share names, not form—
The package now loads calm before the storm.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: exporting Worker from the simpler package root.
Description check ✅ Passed The description is directly related to the changeset and accurately describes the lazy export behavior and documentation updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ChaoWao
ChaoWao force-pushed the feat/export-user-api branch from 469b292 to 7e30c98 Compare July 28, 2026 01:07
@ChaoWao

ChaoWao commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Context: the earlier attempt hit the account review limit, so this PR has not been reviewed yet. It is now rebased onto current main (which carries #1534).

Two things worth a close look:

  1. python/simpler/__init__.py — the PEP 562 __getattr__ is deliberately lazy. simpler/worker.py:83 and simpler/task_interface.py:35 import _task_interface unguarded, so eager exports would make import simpler fail whenever that extension is missing or stale, while _log.py:42-45 deliberately keeps the logging surface working through a fallback.
  2. tests/ut/py/test_package_surface.py — the no-eager-import test asserts on simpler.worker, not on _task_interface. An earlier version asserted the latter and failed, because _log's guarded probe legitimately puts the extension in sys.modules whenever it is importable.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

@ChaoWao I’ll review #1535, focusing in particular on preserving the lazy package surface when the extension is unavailable or stale, and on ensuring the tests distinguish the worker-module import from _log’s legitimate extension probe.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/ut/py/test_package_surface.py (2)

31-33: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid Ruff B018 in the intentional AttributeError test.

Line 33 is deliberately evaluated, but Ruff reports it as a useless expression. Use getattr so the test remains explicit without relying on a lint exception.

Suggested fix
     with pytest.raises(AttributeError, match="has no attribute"):
-        simpler.definitely_not_an_attribute
+        getattr(simpler, "definitely_not_an_attribute")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ut/py/test_package_surface.py` around lines 31 - 33, Update
test_unknown_attribute_raises_attribute_error to trigger the missing attribute
lookup through getattr(simpler, "definitely_not_an_attribute") instead of a
standalone attribute expression, preserving the existing AttributeError
assertion and message match.

Source: Linters/SAST tools


19-23: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover task_interface in the advertised-surface assertion.

Line 22 only checks Worker in dir(simpler), so a broken __dir__ implementation that omitted task_interface would still pass this test.

Suggested assertion
     assert "Worker" in simpler.__all__
     assert "task_interface" in simpler.__all__
     assert "Worker" in dir(simpler)
+    assert "task_interface" in dir(simpler)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ut/py/test_package_surface.py` around lines 19 - 23, Update
test_worker_and_task_interface_are_advertised to also assert that
"task_interface" appears in dir(simpler), covering both advertised symbols in
the module’s runtime surface while preserving the existing __all__ assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/ut/py/test_package_surface.py`:
- Around line 64-70: Restrict the ImportError handling in
test_worker_resolves_to_the_same_object_as_the_submodule to skip only when the
failure indicates the _task_interface extension is unavailable. Re-raise
ImportError instances caused by other dependencies, including cloudpickle,
rather than converting them into skipped tests.

---

Nitpick comments:
In `@tests/ut/py/test_package_surface.py`:
- Around line 31-33: Update test_unknown_attribute_raises_attribute_error to
trigger the missing attribute lookup through getattr(simpler,
"definitely_not_an_attribute") instead of a standalone attribute expression,
preserving the existing AttributeError assertion and message match.
- Around line 19-23: Update test_worker_and_task_interface_are_advertised to
also assert that "task_interface" appears in dir(simpler), covering both
advertised symbols in the module’s runtime surface while preserving the existing
__all__ assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 54ccddf8-ab0f-4b10-a57e-b14274345e84

📥 Commits

Reviewing files that changed from the base of the PR and between cdcbb71 and 7e30c98.

📒 Files selected for processing (5)
  • .claude/rules/project-layout.md
  • docs/user/README.md
  • docs/user/reference/python-api.md
  • python/simpler/__init__.py
  • tests/ut/py/test_package_surface.py

Comment thread tests/ut/py/test_package_surface.py
@ChaoWao
ChaoWao force-pushed the feat/export-user-api branch from 7e30c98 to 5ebab41 Compare July 28, 2026 01:31
`simpler.__all__` advertised only the logging helpers, so the runtime's
own entry point was undiscoverable: `from simpler import Worker` failed
and callers had to know to write `from simpler.worker import Worker`.
The package docstring already called itself the "public Python surface".

`Worker` and the `task_interface` submodule now resolve through a PEP
562 module `__getattr__` rather than eager imports. Both `worker` and
`task_interface` import `_task_interface` (and `worker` also
`cloudpickle`) without a guard, so importing them from `__init__` would
make `import simpler` fail outright whenever that extension is missing
or stale — including for callers that only want the logging helpers,
which `_log` deliberately keeps working through a try/except fallback.

Deliberately not flattened: the task and callable types stay in
`simpler.task_interface`. `simpler.task_interface.Tensor` is a nanobind
device-tensor descriptor while `simpler_setup.Tensor` is a scene-test
argument-spec NamedTuple — same name, unrelated types — so hoisting them
into one namespace would invite exactly the confusion the export is
meant to remove.

project-layout.md described `simpler_setup` as internal test
scaffolding. It is user-facing: examples import `KernelCompiler`,
`SceneTestCase`, `Tensor`, `TaskArgsBuilder`, `ensure_pto_isa_root` and
`make_tensor_arg` from it more often than they import from `simpler`,
and a kernel cannot be compiled without it. The table now says so, and
records the `Tensor` name collision.

tests/ut/py/test_package_surface.py pins the surface: what `__all__`
advertises, that unknown attributes still raise AttributeError, that
`simpler.Worker` is the same object as `simpler.worker.Worker`, and the
two properties the laziness exists for — `import simpler` does not pull
in `simpler.worker`, and it still succeeds with no usable extension. The
first of those asserts on `simpler.worker` rather than on
`_task_interface`, because `_log`'s guarded probe legitimately puts the
extension in `sys.modules` whenever it is importable.

The identity test skips only when `ImportError.name` is
`_task_interface`, and re-raises otherwise: `worker` also imports
cloudpickle, and losing an unrelated dependency has to fail rather than
disappear into a skipped test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ChaoWao

ChaoWao commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai All feedback addressed and folded into the existing single commit.

Inline (Major) — broad except ImportError: fixed, and replied on the thread. Narrowed on exc.name != "_task_interface" rather than a substring of str(exc), since ImportError.name is populated even for the stale-symbol case and so does not depend on CPython's message wording. Verified both directions: stale extension skips, cloudpickle missing re-raises.

Nitpick 31-33 — Ruff B018: taken. Note the repo's select list does not enable B018, so ruff check was already clean; getattr(simpler, ...) is nonetheless clearer about what is being exercised, so it is in, with a # noqa: B009 since that form trades one rule for the other.

Nitpick 19-23 — cover task_interface in dir(): taken. __dir__ returns sorted(__all__), so the assertion would pass today, but the point stands that a __dir__ regression dropping task_interface was previously untested.

Verification: ruff check and ruff format --check clean (including an explicit --select B018,B009 run), tests 5 passed / 1 skipped — the skip being the extension-dependent identity check against this box's stale .so.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@ChaoWao Thanks for addressing all of the feedback. The exc.name guard is a robust way to distinguish the unavailable/stale extension from unrelated import failures, and the added dir() coverage closes the advertised-surface gap. The reported verification results look good.

🐇 ✅


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@ChaoWao
ChaoWao merged commit f293be3 into hw-native-sys:main Jul 28, 2026
16 checks passed
@ChaoWao
ChaoWao deleted the feat/export-user-api branch July 28, 2026 02:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant